#include "node.hpp"

namespace yxml
{
    Node Node::_null( NONE, "Null" );

#define YEPXML_SAFE_NULL(pNode) ((pNode)?(*pNode):_null)


    Node:: ~Node()
    {
        if( _parent )
        {
            if( _parent->_firstChild == this )
                _parent->_firstChild = _nextBrother;
            if( _parent->_lastChild == this )
                _parent->_lastChild = _previousBrother;
            ++_parent->_nChildren;
        }
        if( _nextBrother )
            _nextBrother->_previousBrother = _previousBrother;
        if( _previousBrother )
            _previousBrother->_nextBrother = _nextBrother;
    }

    Node::Node( Type type, const string & value ): _value(value)
    {
        _type = type;
        _firstChild = _lastChild = _nextBrother = _previousBrother = _parent = NULL;
        _nChildren = 0;
    }

    Node & Node::null()
    {
        return _null;
    }

    Node & Node::parent()
    {
        return YEPXML_SAFE_NULL( _parent );
    }

    Node & Node::addAfter( Type type, const string & value )
    {
        if( _type == NONE ) throw "Can't Add elements after NULL";
        if( type == NONE )  throw "Can't Add elements from type NULL";
        Node * newNode = new Node( type, value );
        newNode->_nextBrother = _nextBrother;
        newNode->_previousBrother = this;
        newNode->_parent = _parent;
        if( _nextBrother )_previousBrother->_nextBrother = newNode;
        _nextBrother = newNode;
        if( _parent )
        {
            ++_parent->_nChildren ;
            if( _parent->_lastChild == this ) _parent->_lastChild = newNode;
        }
        return *newNode;
    }

    Node & Node::addBefore( Type type, const string & value )
    {
        if( _type == NONE ) throw "Can't Add elements after NULL";
        if( type == NONE )  throw "Can't Add elements from type NULL";
        Node * newNode = new Node( type, value );
        newNode->_nextBrother = this;
        newNode->_previousBrother = _previousBrother;
        newNode->_parent = _parent;
        if( _previousBrother )_previousBrother->_nextBrother = newNode;
        _previousBrother = newNode;
        if( _parent )
        {
            ++_parent->_nChildren ;
            if( _parent->_firstChild == this ) _parent->_firstChild = newNode;
        }
        return *newNode;
    }


    Node & Node::addChild( Type type, const string & value, int index )
    {
        if( _type != TAG ) throw "Only tags can have children";
        if( index < 0 ) index = _nChildren + index + 1;
        if( index > _nChildren || index < 0 ) throw "Index out of Bounds";
        Node * newNode;
        if( index == 0 )
        {
            newNode = new Node( type, value );
            newNode->_nextBrother = _firstChild;
            newNode->_previousBrother = NULL;
            newNode->_parent = this;
            if( _firstChild ) _firstChild->_previousBrother = newNode;
            _firstChild = newNode;
            if( !_lastChild ) _lastChild = newNode;
        }
        else if( index == _nChildren )
        {
            newNode = new Node( type, value );
            newNode->_previousBrother = _lastChild;
            newNode->_nextBrother = NULL;
            newNode->_parent = this;
            if( _lastChild ) _lastChild->_nextBrother = newNode;
            _lastChild = newNode;
        }
        else
        {
            if( index <= _nChildren/2 )
            {
                Node * beforeNode = _firstChild;
                for( int i = 1; i < index; ++i )
                    beforeNode = beforeNode->_nextBrother;
                newNode = new Node( type, value );
                newNode->_nextBrother = beforeNode->_nextBrother;
                newNode->_previousBrother = beforeNode;
                newNode->_parent = this;
                if( beforeNode->_nextBrother ) beforeNode->_nextBrother->_previousBrother = newNode;
                beforeNode->_nextBrother = newNode;
            }
            else
            {
                Node * afterNode = _firstChild;
                for( int i = _nChildren-1; i > index; --i )
                    afterNode = afterNode->_previousBrother;
                newNode = new Node( type, value );
                newNode->_nextBrother = afterNode;
                newNode->_previousBrother = afterNode->_previousBrother;
                newNode->_parent = this;
                if( afterNode->_previousBrother ) afterNode->_previousBrother->_nextBrother = newNode;
                afterNode->_previousBrother = newNode;
            }
        }
        ++_nChildren;
        return *newNode;
    }

    string Node::toString()
    {
        if( _type & ( COMMENT|TEXT ) )
            return _value;
        else if( _type == TAG )
        {
            string result = "";
            Node * node = _firstChild;
            while( node )
            {
                if( node->_type == TEXT ) result += node->_value;
                node = node->_nextBrother;
            }
            return result;
        }
        return "";
    }


    Node & Node::next( int types )
    {
        Node * node = this;
        do
        {
            if( node->_firstChild ) node = node->_firstChild;
            else if( node->_nextBrother ) node = node->_nextBrother;
            else
            {
                while( node->_parent )
                {
                    if(node->_parent->_nextBrother)
                    {
                        node = node->_parent->_nextBrother;
                        break;
                    }
                    else
                        node = node->_parent;
                }
                if( !node ) return _null; //Or a goto?
            }
        }
        while( !( node->_type & types ) );
        return *node;
    }

    Node & Node::previous( int types )
    {
        Node * node = this;
        do
        {
            if( node->_previousBrother)
            {
                node = node->_previousBrother;
                while(node->_lastChild)
                    node = node->_lastChild;
            }
            else if( node->_parent)
                node = node->_parent;
            else
                return _null;
//            if( node->_lastChild ) node = node->_lastChild;
//            else if( node->_previousBrother ) node = node->_previousBrother;
//            else if( node->_parent ) node = node->_parent;
//            else return _null;
        }
        while( !( node->_type & types ) );
        return *node;
    }

    Node & Node::nextBrother( int types )
    {
        Node * node = this;
        do
        {
            node = node->_nextBrother;
            if( node == NULL ) return _null;
        }
        while( !( node->_type & types ) );
        return *node;
    }

    Node & Node::previousBrother( int types )
    {
        Node * node = this;
        do
        {
            node = node->_previousBrother;
            if( node == NULL ) return _null;
        }
        while( !( node->_type & types ) );
        return *node;
    }

    Node & Node::firstChild( int types )
    {
        Node * node = _firstChild;
        while( node && !( node->_type & types ) )
        {
            node = node->_nextBrother;
            if( node == NULL ) return _null;
        }
        return *node;
    }

    Node & Node::lastChild( int types )
    {
        Node * node = _lastChild;
        while( node && !( node->_type & types ) )
        {
            node = node->_previousBrother;
            if( node == NULL ) return _null;
        }
        return *node;
    }


    Node & Node::operator[]( int index )
    {
        int count = 0;
        Node * node = _firstChild;
        while( node )
        {
            if( node->_type == TAG )
            {
                if( count == index )
                    return *node;
                ++count;
            }
            node = node->_nextBrother;
        }
        return _null;
    }

    Node & Node::operator[]( string value )
    {
        Node * node = _firstChild;
        while( node )
        {
            if( node->_value == value && node->_type==TAG )
                return *node;
            node = node->_nextBrother;
        }
        return _null;
    }

    Node & Node::operator[]( pair<string, int> valuePosition )
    {
        int count = 0;
        Node * node = _firstChild;
        while( node )
        {
            if( node->_value == valuePosition.first && node->_type==TAG )
            {
                if( count == valuePosition.second )
                    return *node;
                ++count;
            }
            node = node->_nextBrother;
        }
        return _null;
    }

    int Node::count()
    {
        return _nChildren;
    }

    int Node::count( int types )
    {
        Node * node = _firstChild;
        int count = 0;
        while ( node )
        {
            if( node->_type & types )
                ++count;
            node = node->_nextBrother;
        }
        return count;
    }


    int Node::count( const string & value, int types )
    {
        Node * node = _firstChild;
        int count = 0;
        while ( node )
        {
            if( node->_type & types && node->_value == value)
                ++count;
            node = node->_nextBrother;
        }
        return count;
    }

    Node::Node( const Node & other )
    {
        _type = other._type;
        _firstChild = _lastChild = _nextBrother = _previousBrother = _parent = NULL;
        _nChildren = other._nChildren;
        if( _nChildren )
        {
            Node * node = other._firstChild;
            Node * newNode = new Node( *node );
            _firstChild = newNode;
            newNode->_parent = this;
            newNode->_previousBrother = NULL;
            for( int i=1; i< _nChildren; ++i )
            {
                node = node->_nextBrother;
                Node * before = newNode;
                newNode = new Node( *node );
                newNode->_parent = this;
                newNode->_previousBrother = before;
                before->_nextBrother = newNode;
            }
            newNode->_nextBrother = NULL;
            _lastChild = newNode;
        }
    }

    size_t Node::attributesCount() const
    {
        return _attributes.size();
    }

    map<string, string>::const_iterator Node::attributeBegin() const
    {
        return _attributes.begin();
    }

    map<string, string>::const_iterator Node::attributeEnd() const
    {
        return _attributes.end();
    }

}
